local FileSystem = Tubes.FileSystem
FileSystem._Folder = {}
local Folder = FileSystem._Folder
Folder.__index = Folder
setmetatable (Folder, Tubes.FileSystem._Base)

FileSystem.Folder = Tubes.MakeConstructor (Folder)

-- Networking
local StringPool = Tubes.StringPool (65536)
local ContentCallbacks = {}

function Folder:ctor (name)
	FileSystem._Base.ctor (self, name)
	
	self.Populated = false
	
	self.Type = "Folder"
	self.Name = name or ""
	self.Children = {}
end

function Folder:AddItem (item)
	self.Children [item:GetName ()] = item
	item:SetParent (self)
	item:SetOwner (self:GetOwner ())
	if item:IsFolder () then
		self.FolderCount = self.FolderCount + 1
	else
		self.FileCount = self.FileCount + 1
	end
	self:DispatchEvent ("ItemAdded", self, item)
	return item
end

function Folder:CreateFile (name)
	local Parts = string.Explode ("/", name:gsub ("\\", "/"))
	local Folder = self:CreateFolder (nil, name, true)
	local File = nil
	local Name = Parts [#Parts]
	if Folder then
		if Folder:ItemExists (Name) then
			return Folder.Children [Name]
		end
		Folder:AddItem (FileSystem.File (Name))
	end
	return File
end

function Folder:CreateFolder (ply, name, ignoreLast)
	local Parts = string.Explode ("/", name:gsub ("\\", "/"))
	local Current = self
	local PartCount = #Parts
	if ignoreLast then
		PartCount = PartCount - 1
	end
	for i = 1, PartCount do
		local Name = Parts [i]
		if Name ~= "" then
			Current = Current:CreateFolderDirect (ply, Name)
			if not Current then
				break
			end
		end
	end
	return Current
end

function Folder:CreateFolderDirect (ply, name)
	if self.Children [name] then
		return self.Children [name]:IsFolder () and self.Children [name]
	end
	return self:AddItem (FileSystem.Folder (name))
end

function Folder:GetChildIterator (ply, callback)
	if not self:PlayerHasPermission (ply, "View Folder") then
		callback (nil, "Access Denied")
		return
	end
	if not self:IsPopulated () then
		if CLIENT and self:GetOwner () ~= LocalPlayer () or
			not self:IsOwnerServer () then
			local Path = self:GetFullPath ()
			if ContentCallbacks [Path] then
				ContentCallbacks [Path] [#ContentCallbacks [Path] + 1] = callback
			else
				Tubes.Networking.Send ("FolderRequest", self:GetOwner  (), ply, Path, callback)
			end
			if self:GetItemCount () == 0 then
				return
			end
		end
	end
	local Next, Table, Key = pairs (self.Children)
	callback (function ()
		Key = Next (Table, Key)
		return Key, Table [Key]
	end)
end

function Folder:GetFileCount ()
	return self.FileCount
end

function Folder:GetFolderCount ()
	return self.FolderCount
end

function Folder:GetItem (name)
	local parts = string.Explode ("/", name:gsub ("\\", "/"))
	local current = self
	for i = 1, #parts do
		local name = parts [i]
		if name ~= "" then
			if not current:ItemExists (name) then
				return nil
			else
				current = current.Children [name]
			end
		end
	end
	return current
end

function Folder:GetItemCount ()
	return self.FileCount + self.FolderCount
end

function Folder:IsFolder ()
	return true
end

function Folder:IsPopulated ()
	return self.Populated
end

function Folder:ItemExists (name)
	return self.Children [name] and true or false
end

function Folder:MarkPopulated (populated)
	self.Populated = populated == nil and true or false
end

function Folder:Mount (folder)
	if not folder:GetParent () then
		folder:SetParent (self)
		folder:SetOwner (self:GetOwner ())
	end
	self.Children [folder:GetName ()] = folder
	self.FolderCount = self.FolderCount + 1
end

function Folder:Refresh (ply)
	self:MarkPopulated (false)
	self:GetChildIterator (ply, function () end)
end

Tubes.Networking.RegisterReader ("FolderContents", function (netMessage)
	local Index = netMessage:UShort ()
	local Allowed = netMessage:UChar ()
	local Path = StringPool:LookupString (Index)
	local Folder = Tubes.FileSystem.GetRoot ():GetItem (Path)
	
	if Allowed > 0 then
		while true do
			local IsFolder, HasNiceName, HasPermissions, End = netMessage:BitField ()
			if End then
				break
			end
			local ItemName = netMessage:String ()
			local Item = nil
			if IsFolder then
				print ("Folder: " .. ItemName)
				Item = Folder:CreateFolder (nil, ItemName)
			else
				print ("File: " .. ItemName)
				Item = Folder:CreateFile (ItemName)
			end
			if HasNiceName then
				local NiceName = netMessage:String ()
				Item:SetNiceName (NiceName)
				print ("Nice: " .. NiceName)
			end
			if HasPermissions then
				Item:GetPermissions ():Receive (netMessage)
			end
		end
		
		Folder:MarkPopulated ()
		Folder:GetChildIterator (ContentCallbacks [Path].Player, function (iterator)
			for _, Callback in ipairs (ContentCallbacks [Path]) do
				Callback (iterator)
			end
		end)
		ContentCallbacks [Path] = nil
		StringPool:RemoveString (Path)
	else
		for _, Callback in ipairs (ContentCallbacks [Path]) do
			Callback (nil, "Access Denied")
		end
	end
end)

Tubes.Networking.RegisterSender ("FolderContents", function (netMessage, path, iterator)
	netMessage:UShort (StringPool:ComputeIndex (path))
	netMessage:UChar (iterator and 1 or 0)
	if not iterator then
		return
	end
	for ItemName, Item in iterator do
		local IsFolder = Item:IsFolder ()
		local HasNiceName = Item:HasNiceName ()
		local HasPermissions = Item:GetOwner () ~= Item:GetParent ():GetOwner () or not Item:GetPermissions ():IsEmpty ()
		
		netMessage:BitField (IsFolder, HasNiceName, HasPermissions)
		netMessage:String (ItemName)
		if HasNiceName then
			netMessage:String (Item:GetNiceName ())
		end
		if HasPermissions then
			Item:GetPermissions ():Send (netMessage)
		end
	end
	netMessage:UChar (255)
end)

Tubes.Networking.RegisterReader ("FolderRequest", function (netMessage)
	local Path = netMessage:String ()
	ErrorNoHalt ("Received request for " .. Path .. "\n")
	local Folder = Tubes.FileSystem.GetRoot ():GetItem (Path)
	if not Folder then
		return
	end
	local Source = netMessage:Player ()
	if SERVER then
		Source = netMessage:GetSource ()
	end
	Folder:GetChildIterator (Source, function (iterator)
		Tubes.Networking.Send ("FolderContents", netMessage:GetSource (), Path, iterator)
	end)
end)

Tubes.Networking.RegisterSender ("FolderRequest", function (netMessage, ply, path, callback)
	netMessage:String (path)
	netMessage:Player (ply)
	
	ContentCallbacks [path] = ContentCallbacks [path] or {Player = ply}
	ContentCallbacks [path] [#ContentCallbacks [path] + 1] = callback
	StringPool:AddString (path)
end)